'use client';

import { useState, useEffect, useCallback } from 'react';
import { ethers } from 'ethers';
import { toast } from 'react-hot-toast';
import type { CreateBSCTokenParams, BSCTradeParams, BSCBuyPreview, BSCSellPreview } from '@/types/bsc';
import { apiClient } from '@/lib/api';

// BSC network configuration
const BSC_NETWORK = {
  chainId: 97, // BSC Testnet, change to 56 for mainnet
  chainName: 'BSC Testnet',
  nativeCurrency: {
    name: 'BNB',
    symbol: 'BNB',
    decimals: 18,
  },
  rpcUrls: ['https://data-seed-prebsc-1-s1.binance.org:8545/'],
  blockExplorerUrls: ['https://testnet.bscscan.com/'],
};

// Contract addresses and ABI
const LAUNCHPAD_ADDRESS = '0xYourBSCLaunchPadAddressHere'; // TODO: Update after deployment
const LAUNCHPAD_ABI = [
  // Basic view functions
  'function nextLaunchId() view returns (uint256)',
  'function platformFeePercent() view returns (uint256)',
  'function MIN_TARGET() view returns (uint256)',
  'function MAX_TARGET() view returns (uint256)',
  'function CURVE_BASE_PRICE() view returns (uint256)',
  'function CURVE_PRICE_INCREMENT() view returns (uint256)',
  
  // Launch functions
  'function createTokenLaunch(string name, string symbol, string description, string website, string telegram, string twitter, uint256 targetAmount) payable',
  'function buyTokens(uint256 launchId) payable',
  'function sellTokens(uint256 launchId, uint256 tokenAmount)',
  'function manualGraduateToken(uint256 launchId)',
  'function cancelLaunch(uint256 launchId)',
  'function claimRefund(uint256 launchId)',
  
  // View functions
  'function getLaunchInfo(uint256 launchId) view returns (address tokenAddress, address creator, string name, string symbol, string description, uint256 targetAmount, uint256 raisedAmount, uint256 deadline, bool launched, bool cancelled, uint256 currentPrice, uint256 tokensSold, uint256 totalSupplyForSale)',
  'function getUserContribution(uint256 launchId, address user) view returns (uint256, uint256)',
  'function getCurrentTokenPrice(uint256 launchId) view returns (uint256)',
  'function getBuyPreview(uint256 launchId, uint256 bnbAmount) view returns (uint256 tokensReceived, uint256 newPrice)',
  'function getSellPreview(uint256 launchId, uint256 tokenAmount) view returns (uint256 bnbReceived, uint256 newPrice)',
  
  // Events
  'event TokenLaunchCreated(uint256 indexed launchId, address indexed creator, address tokenAddress, string name, string symbol, uint256 targetAmount)',
  'event TokenPurchase(uint256 indexed launchId, address indexed buyer, uint256 bnbAmount, uint256 tokensReceived, uint256 newPrice)',
  'event TokenSale(uint256 indexed launchId, address indexed seller, uint256 tokensAmount, uint256 bnbReceived, uint256 newPrice)',
];

const ERC20_ABI = [
  'function balanceOf(address owner) view returns (uint256)',
  'function symbol() view returns (string)',
  'function name() view returns (string)',
  'function decimals() view returns (uint8)',
  'function approve(address spender, uint256 amount) returns (bool)',
  'function transfer(address to, uint256 amount) returns (bool)',
];

declare global {
  interface Window {
    ethereum?: any;
  }
}

export function useBSCWallet() {
  const [isConnected, setIsConnected] = useState(false);
  const [address, setAddress] = useState<string | null>(null);
  const [balance, setBalance] = useState<string>('0');
  const [provider, setProvider] = useState<ethers.BrowserProvider | null>(null);
  const [signer, setSigner] = useState<ethers.JsonRpcSigner | null>(null);
  const [isLoading, setIsLoading] = useState(false);

  // Check if wallet is connected on mount
  useEffect(() => {
    checkConnection();
  }, []);

  const checkConnection = async () => {
    if (typeof window !== 'undefined' && window.ethereum) {
      try {
        const accounts = await window.ethereum.request({ method: 'eth_accounts' });
        if (accounts.length > 0) {
          await connectWallet();
        }
      } catch (error) {
        console.error('Error checking connection:', error);
      }
    }
  };

  const addBSCNetwork = async () => {
    if (!window.ethereum) return false;
    
    try {
      await window.ethereum.request({
        method: 'wallet_addEthereumChain',
        params: [{
          chainId: `0x${BSC_NETWORK.chainId.toString(16)}`,
          chainName: BSC_NETWORK.chainName,
          nativeCurrency: BSC_NETWORK.nativeCurrency,
          rpcUrls: BSC_NETWORK.rpcUrls,
          blockExplorerUrls: BSC_NETWORK.blockExplorerUrls,
        }],
      });
      return true;
    } catch (error) {
      console.error('Failed to add BSC network:', error);
      return false;
    }
  };

  const switchToBSC = async () => {
    if (!window.ethereum) return false;
    
    try {
      await window.ethereum.request({
        method: 'wallet_switchEthereumChain',
        params: [{ chainId: `0x${BSC_NETWORK.chainId.toString(16)}` }],
      });
      return true;
    } catch (error: any) {
      // If network doesn't exist, add it
      if (error.code === 4902) {
        return await addBSCNetwork();
      }
      console.error('Failed to switch to BSC network:', error);
      return false;
    }
  };

  const connectWallet = async () => {
    if (!window.ethereum) {
      toast.error('请安装 MetaMask 或其他兼容钱包');
      return false;
    }

    setIsLoading(true);
    
    try {
      // Request account access
      await window.ethereum.request({ method: 'eth_requestAccounts' });
      
      // Switch to BSC network
      const networkSwitched = await switchToBSC();
      if (!networkSwitched) {
        toast.error('请切换到 BSC 网络');
        setIsLoading(false);
        return false;
      }

      // Create provider and signer
      const web3Provider = new ethers.BrowserProvider(window.ethereum);
      const web3Signer = await web3Provider.getSigner();
      const userAddress = await web3Signer.getAddress();
      
      setProvider(web3Provider);
      setSigner(web3Signer);
      setAddress(userAddress);
      setIsConnected(true);
      
      // Get balance
      const userBalance = await web3Provider.getBalance(userAddress);
      setBalance(ethers.formatEther(userBalance));
      
      toast.success('钱包连接成功');
      return true;
    } catch (error: any) {
      console.error('Failed to connect wallet:', error);
      toast.error('钱包连接失败: ' + (error.message || '未知错误'));
      return false;
    } finally {
      setIsLoading(false);
    }
  };

  const disconnect = () => {
    setIsConnected(false);
    setAddress(null);
    setBalance('0');
    setProvider(null);
    setSigner(null);
    toast.success('');
  };

  const createTokenLaunch = useCallback(async (params: CreateBSCTokenParams & { imageUrl?: string }) => {
    if (!signer || !isConnected) {
      throw new Error('Wallet not connected');
    }

    try {
      // First upload image if exists
      let imageUrl = params.imageUrl;
      if (params.imageFile && !imageUrl) {
        const uploadResult = await apiClient.uploadBSCImage(params.imageFile);
        imageUrl = uploadResult.url;
      }

      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, signer);
      
      // Convert target amount to wei
      const targetAmountWei = ethers.parseEther(params.targetAmount);
      
      // Creation fee is 0.01 BNB
      const creationFee = ethers.parseEther('0.01');
      
      const tx = await contract.createTokenLaunch(
        params.name,
        params.symbol,
        params.description,
        params.website || '',
        params.telegram || '',
        params.twitter || '',
        targetAmountWei,
        { value: creationFee }
      );
      
      const receipt = await tx.wait();
      
      // Parse the TokenLaunchCreated event
      let launchId = 0;
      let tokenAddress = '';
      
      const event = receipt.logs.find((log: any) => {
        try {
          const parsedLog = contract.interface.parseLog(log);
          return parsedLog?.name === 'TokenLaunchCreated';
        } catch {
          return false;
        }
      });
      
      if (event) {
        const parsedEvent = contract.interface.parseLog(event);
        launchId = parsedEvent?.args.launchId.toNumber();
        tokenAddress = parsedEvent?.args.tokenAddress;
      }

      // Save to database via API
      try {
        await apiClient.createBSCToken({
          ...params,
          imageUrl
        });
      } catch (apiError) {
        console.error('Failed to save token to database:', apiError);
        // Continue even if API fails, as the contract transaction succeeded
      }
      
      return {
        success: true,
        launchId,
        tokenAddress,
        txHash: receipt.hash
      };
    } catch (error: any) {
      console.error('Token creation error:', error);
      return {
        success: false,
        error: error.message || '创建失败'
      };
    }
  }, [signer, isConnected, address]);

  const buyTokens = useCallback(async (params: BSCTradeParams) => {
    if (!signer || !isConnected) {
      throw new Error('钱包未连接');
    }

    if (params.tradeType !== 'buy') {
      throw new Error('Invalid trade type for buyTokens');
    }

    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, signer);
      const bnbAmount = ethers.parseEther(params.amount);
      
      const tx = await contract.buyTokens(params.launchId, { value: bnbAmount });
      const receipt = await tx.wait();
      
      // Parse the TokenPurchase event
      let tokensReceived = '0';
      let newPrice = '0';
      
      const event = receipt.logs.find((log: any) => {
        try {
          const parsedLog = contract.interface.parseLog(log);
          return parsedLog?.name === 'TokenPurchase';
        } catch {
          return false;
        }
      });
      
      if (event) {
        const parsedEvent = contract.interface.parseLog(event);
        tokensReceived = ethers.formatEther(parsedEvent?.args.tokensReceived || '0');
        newPrice = ethers.formatEther(parsedEvent?.args.newPrice || '0');
      }

      // Update database via API
      try {
        await apiClient.contributeToBSCToken(params.launchId.toString(), {
          contributor: address!,
          amount: params.amount,
          txHash: receipt.hash
        });
      } catch (apiError) {
        console.error('Failed to update purchase in database:', apiError);
        // Continue even if API fails, as the contract transaction succeeded
      }
      
      return {
        success: true,
        tokensReceived,
        bnbSpent: params.amount,
        newPrice,
        txHash: receipt.hash
      };
    } catch (error: any) {
      console.error('Buy error:', error);
      return {
        success: false,
        error: error.message || '购买失败'
      };
    }
  }, [signer, isConnected, address]);

  const sellTokens = useCallback(async (params: BSCTradeParams) => {
    if (!signer || !isConnected) {
      throw new Error('钱包未连接');
    }

    if (params.tradeType !== 'sell') {
      throw new Error('Invalid trade type for sellTokens');
    }

    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, signer);
      const tokenAmount = ethers.parseEther(params.amount);
      
      const tx = await contract.sellTokens(params.launchId, tokenAmount);
      const receipt = await tx.wait();
      
      // Parse the TokenSale event
      let bnbReceived = '0';
      let newPrice = '0';
      
      const event = receipt.logs.find((log: any) => {
        try {
          const parsedLog = contract.interface.parseLog(log);
          return parsedLog?.name === 'TokenSale';
        } catch {
          return false;
        }
      });
      
      if (event) {
        const parsedEvent = contract.interface.parseLog(event);
        bnbReceived = ethers.formatEther(parsedEvent?.args.bnbReceived || '0');
        newPrice = ethers.formatEther(parsedEvent?.args.newPrice || '0');
      }

      // Update database via API
      try {
        await apiClient.contributeToBSCToken(params.launchId.toString(), {
          contributor: address!,
          amount: bnbReceived, // Use BNB received as amount for sell
          txHash: receipt.hash
        });
      } catch (apiError) {
        console.error('Failed to update sale in database:', apiError);
        // Continue even if API fails, as the contract transaction succeeded
      }
      
      return {
        success: true,
        bnbReceived,
        newPrice,
        txHash: receipt.hash
      };
    } catch (error: any) {
      console.error('Sell error:', error);
      return {
        success: false,
        error: error.message || '出售失败'
      };
    }
  }, [signer, isConnected, address]);

  const getBuyPreview = useCallback(async (launchId: number, bnbAmount: string): Promise<BSCBuyPreview | null> => {
    if (!provider) return null;
    
    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, provider);
      const bnbAmountWei = ethers.parseEther(bnbAmount);
      const [tokensReceived, newPrice] = await contract.getBuyPreview(launchId, bnbAmountWei);
      
      const currentPrice = await contract.getCurrentTokenPrice(launchId);
      const priceImpact = ((parseFloat(ethers.formatEther(newPrice)) - parseFloat(ethers.formatEther(currentPrice))) / parseFloat(ethers.formatEther(currentPrice))) * 100;
      
      return {
        tokensReceived: ethers.formatEther(tokensReceived),
        newPrice: ethers.formatEther(newPrice),
        priceImpact
      };
    } catch (error) {
      console.error('Failed to get buy preview:', error);
      return null;
    }
  }, [provider]);

  const getSellPreview = useCallback(async (launchId: number, tokenAmount: string): Promise<BSCSellPreview | null> => {
    if (!provider) return null;
    
    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, provider);
      const tokenAmountWei = ethers.parseEther(tokenAmount);
      const [bnbReceived, newPrice] = await contract.getSellPreview(launchId, tokenAmountWei);
      
      const currentPrice = await contract.getCurrentTokenPrice(launchId);
      const priceImpact = ((parseFloat(ethers.formatEther(currentPrice)) - parseFloat(ethers.formatEther(newPrice))) / parseFloat(ethers.formatEther(currentPrice))) * 100;
      
      return {
        bnbReceived: ethers.formatEther(bnbReceived),
        newPrice: ethers.formatEther(newPrice),
        priceImpact
      };
    } catch (error) {
      console.error('Failed to get sell preview:', error);
      return null;
    }
  }, [provider]);

  const getTokenBalance = useCallback(async (tokenAddress: string, userAddress?: string) => {
    if (!provider) return '0';
    
    try {
      const contract = new ethers.Contract(tokenAddress, ERC20_ABI, provider);
      const balance = await contract.balanceOf(userAddress || address);
      return ethers.formatEther(balance);
    } catch (error) {
      console.error('Failed to get token balance:', error);
      return '0';
    }
  }, [provider, address]);

  const getLaunchInfo = useCallback(async (launchId: number) => {
    if (!provider) return null;
    
    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, provider);
      const info = await contract.getLaunchInfo(launchId);
      
      return {
        tokenAddress: info[0],
        creator: info[1],
        name: info[2],
        symbol: info[3],
        description: info[4],
        targetAmount: ethers.formatEther(info[5]),
        raisedAmount: ethers.formatEther(info[6]),
        deadline: info[7].toNumber(),
        launched: info[8],
        cancelled: info[9],
        currentPrice: ethers.formatEther(info[10]),
        tokensSold: ethers.formatEther(info[11]),
        totalSupplyForSale: ethers.formatEther(info[12])
      };
    } catch (error) {
      console.error('Failed to get launch info:', error);
      return null;
    }
  }, [provider]);

  const getUserContribution = useCallback(async (launchId: number, userAddress?: string) => {
    if (!provider) return { contribution: '0', tokenBalance: '0' };
    
    try {
      const contract = new ethers.Contract(LAUNCHPAD_ADDRESS, LAUNCHPAD_ABI, provider);
      const [contribution, tokenBalance] = await contract.getUserContribution(launchId, userAddress || address);
      return {
        contribution: ethers.formatEther(contribution),
        tokenBalance: ethers.formatEther(tokenBalance)
      };
    } catch (error) {
      console.error('Failed to get user contribution:', error);
      return { contribution: '0', tokenBalance: '0' };
    }
  }, [provider, address]);

  return {
    isConnected,
    address,
    balance,
    provider,
    signer,
    isLoading,
    connect: connectWallet,
    disconnect,
    createTokenLaunch,
    buyTokens,
    sellTokens,
    getBuyPreview,
    getSellPreview,
    getTokenBalance,
    getLaunchInfo,
    getUserContribution
  };
}